package com.share.filter;


import com.share.constant.SecurityConstant;
import com.share.exception.CustomerAuthenticationException;
import com.share.handler.CustomLoginAuthenticationFailureHandler;
import com.share.properties.Whitelist;
import com.share.user.admin.dtos.PayloadDTO;
import com.share.util.thread.ThreadLocalUtils;
import com.share.utils.TokenUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.*;


/**
 * 统一访问处理器 DispatcherServlet之前
 * 使用于token鉴权
 */
@Slf4j
@Component
@Order(-1)
public class CustomAuthorizationFilter extends OncePerRequestFilter {

    //模糊匹配
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    private final Whitelist whitelist;
    private final CustomLoginAuthenticationFailureHandler customLoginAuthenticationFailureHandler;
    private final TokenUtils tokenUtils;

    public CustomAuthorizationFilter(Whitelist whitelist, CustomLoginAuthenticationFailureHandler customLoginAuthenticationFailureHandler, TokenUtils tokenUtils) {
        this.whitelist = whitelist;
        this.customLoginAuthenticationFailureHandler = customLoginAuthenticationFailureHandler;
        this.tokenUtils = tokenUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            //对当前路由进行匹配 如果在白名单直接放行 不在 需要查看是否有token
            String requestURI = request.getRequestURI();
            if (whitelist.getExcludeList().stream().anyMatch(path -> pathMatcher.match(path, requestURI))) {
                filterChain.doFilter(request, response);
                return;
            }
            try {
                validToken(request);
            } catch (AuthenticationException e) {
                customLoginAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);
            }
            filterChain.doFilter(request, response);
        } finally {
            ThreadLocalUtils.remove();
        }
    }


    private void validToken(HttpServletRequest request) {
        //从请求头中获取token
        String token = request.getHeader(SecurityConstant.AUTHORIZATION_KEY);
        if (Objects.isNull(token)) { //如果header中没有token
            token = request.getParameter(SecurityConstant.AUTHORIZATION_KEY);
        }
        //进行一系列判断验证
        if (Objects.isNull(token)) {
            throw new CustomerAuthenticationException("token为空");
        }

        if (token.startsWith("Bearer ")) {
            token = token.substring(7);
        } else {
            throw new CustomerAuthenticationException("token无效,格式不正确");
        }

        if (!tokenUtils.isTokenExpired(token)) {
            try {
                PayloadDTO payloadDto = tokenUtils.getClaimsFromToken(token);
                ThreadLocalUtils.set(payloadDto);
                Optional<PayloadDTO> optionalPayloadDto = Optional.ofNullable(payloadDto);
                String username = null;
                List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                username = optionalPayloadDto.map(PayloadDTO::getUsername)
                        .orElseThrow(() -> new CustomerAuthenticationException("用户名获取为 null，token 校验失败"));

                if (Objects.equals(payloadDto.getType(), SecurityConstant.USER_TYPE_BLOG)) {
                    // 管理端用户走这条
                    authorities = optionalPayloadDto.map(PayloadDTO::getAuthorities)
                            .orElse(Collections.emptyList())
                            .stream()
                            .map(SimpleGrantedAuthority::new)
                            .toList();
                } else {
                    // 其他用户登陆走这条
                    authorities.add(new SimpleGrantedAuthority(SecurityConstant.DEFAULT_PERMISSIONS));
                }

                UsernamePasswordAuthenticationToken authenticationToken =
                        new UsernamePasswordAuthenticationToken(username, null, authorities);
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            } catch (CustomerAuthenticationException e) {
                throw new CustomerAuthenticationException("token校验失败");
            }

        } else {
            throw new CustomerAuthenticationException("token过期,请重新登陆");
        }
    }

}
